package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"runtime/debug"
	"strings"
	"sync"
	"time"

	"github.com/PuerkitoBio/goquery"
	"github.com/go-dynamic-proxy/common/constant"
	"github.com/go-dynamic-proxy/common/util"
	"github.com/robfig/cron"
)

const (
	nt = "https://www.xicidaili.com/nt/%v"
	nn = "https://www.xicidaili.com/nn/%v"
	wn = "https://www.xicidaili.com/wn/%v"
	wt = "https://www.xicidaili.com/wt/%v"
)

var mu sync.Mutex

func init() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
}
func main() {
	initVerifyData := flag.Bool("initVerifyData", false, "是否需要初始化的时候更新数据")
	initParseData := flag.Bool("initParseData", false, "是否需要初始化的时候更新数据")
	flag.Parse()
	if *initVerifyData {
		log.Println("verify begin")
		Verify()
		log.Println("verify end")
	}
	if *initParseData {
		log.Println("parse begin")
		Parse()
		log.Println("parse ends")
	}

	cronTask := cron.New()
	cronTask.AddFunc("@every 18h", func() {
		mu.Lock()
		defer mu.Unlock()
		log.Println("parse begin")
		Parse()
		log.Println("parse ends")
	})
	cronTask.AddFunc("@every 1h", func() {
		log.Println("verify begin")
		Verify()
		log.Println("verify end")
	})
	cronTask.Start()
	select {}
}

//Parse 解析西丽代理的ip地址
func Parse() {
	defer func() {
		if err := recover(); err != nil {
			log.Println(err)
			debug.PrintStack()
		}
	}()
	fdtmp, err := os.OpenFile(constant.IPFILE, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		panic(err)
	}
	var proxyurlmap = make(map[string]string)
	b := bufio.NewReader(fdtmp)
	for {
		result, err := b.ReadString('\n')
		if err != nil || err == io.EOF {
			break
		}
		if strings.LastIndex(result, "\n") > 0 {
			a := result[:strings.LastIndex(result, "\n")]
			log.Println(a)
			proxyurlmap[a] = ""
		}

	}
	defer fdtmp.Close()
	var proxyurls = []string{nt, nn, wn, wt}
	for _, proxyurl := range proxyurls {
		for i := 1; ; i++ {
			resp, err := util.HTTPRequest(http.MethodGet, fmt.Sprintf(proxyurl, i), "", nil, nil, nil)
			if err != nil {
				panic(err)
			}

			document, err := goquery.NewDocumentFromReader(bytes.NewReader(resp))

			if err != nil {
				log.Println(err)
				return
			}
			if document.Find("table#ip_list>tbody>tr").Length() == 0 { //最后一页
				continue
			}

			document.Find("table#ip_list>tbody>tr").Each(func(i int, s *goquery.Selection) {
				ip, port, scheme := "", "", ""
				s.Find("td").Each(func(i int, s *goquery.Selection) {
					if i == 1 {
						ip = strings.TrimSpace(s.Text())
					}
					if i == 2 {
						port = strings.TrimSpace(s.Text())
					}
					if i == 5 {
						scheme = strings.TrimSpace(s.Text())
					}
					log.Println(i, s.Text())
				})
				if len(ip) == 0 {
					return
				}
				if _, ok := proxyurlmap[ip+" "+port+" "+scheme]; ok {
					return
				}
				fdtmp.WriteString(ip + " " + port + " " + scheme + "\n")
			})
		}
	}
}

//Verify 验证ip地址的可靠性
func Verify() {
	defer func() {
		if err := recover(); err != nil {
			log.Println(err)
			debug.PrintStack()
		}
	}()
	file, err := os.OpenFile(constant.IPFILE, os.O_RDONLY, 0644)
	defer file.Close()

	verifyFileTMP, err := os.OpenFile(constant.VERIFYIPFILE_TMP, os.O_RDWR|os.O_CREATE|os.O_APPEND|os.O_TRUNC, 0644)
	defer verifyFileTMP.Close()

	if err != nil {
		panic(err)
	}
	read := bufio.NewReader(file)
	for {
		err := func() error {
			defer func() {
				if err := recover(); err != nil {
					log.Println(err)
					debug.PrintStack()
				}
			}()
			res, fileReadErr := read.ReadSlice('\n')
			if fileReadErr != nil && io.EOF == fileReadErr { //文件读结束
				return io.EOF
			}
			ipPort := strings.ReplaceAll(string(res), " ", ":")
			arr := strings.Split(string(res), " ")
			if len(arr) < 3 {
				return nil
			}
			ip := arr[0]
			port := arr[1]
			ipPort = strings.TrimSpace(ip) + ":" + strings.TrimSpace(port)
			c, err := net.DialTimeout("tcp", ipPort, 2*time.Second)
			if c == nil || err != nil {
				log.Println(err)
				return nil
			}
			defer c.Close()
			req, err := http.NewRequest(http.MethodConnect, "https://www.baidu.com", nil) //通过代理测试是否能访问百度主页
			if err != nil {
				log.Println(err)
				return nil
			}
			req.Header.Set("User-Agent", "Powerby Dota")
			err = req.Write(c)
			if err != nil {
				return nil
			}
			resp, err := http.ReadResponse(bufio.NewReader(c), req)
			if err != nil {
				log.Println(err)
				return nil
			}
			defer resp.Body.Close()
			if resp.StatusCode != 200 {
				log.Printf("Connect server using proxy error, StatusCode [%d]\n", resp.StatusCode)
				return nil
			}
			log.Println("验证可用地址:", string(res))
			verifyFileTMP.WriteString(string(res))
			return nil
		}()
		if err == io.EOF {
			break
		}
	}

	log.Println("验证地址完毕", os.Rename(constant.VERIFYIPFILE_TMP, constant.VERIFYIPFILE))

}
